/*
 * UdpSendDelay.cpp
 *
 *  Created on: 2019-04-02
 *      Author: chuanjiang.zh
 */

#include "UdpSendDelay.h"
#include "CLog.h"

#ifdef WIN32
#else
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#endif //

namespace av
{

UdpSendDelay::UdpSendDelay():
    m_continuation(CONTINUATION_PACKET_COUNT),
    m_startClock(),
    m_lastClock(),
    m_pktCount()
{
}

UdpSendDelay::~UdpSendDelay()
{
}

void UdpSendDelay::setContinuation(int pktCount)
{
    if (pktCount <= 0)
    {
        return;
    }

    m_continuation = pktCount;
}

int UdpSendDelay::send(int size)
{
    int64_t clock = getClock();

    if (m_startClock <= 0)
    {
        m_startClock = clock;
        m_lastClock = clock;
        m_pktCount = 1;

        return 0;
    }
    
    int64_t duration = clock - m_lastClock;
    if (duration >= 1000)
    {
        m_startClock = clock;
        m_lastClock = clock;
        m_pktCount = 1;

        return 0;
    }

    m_lastClock = clock;

    m_pktCount++;

    int ms = m_pktCount / m_continuation;

    if (ms > 0)
    {
        //CLog::debug("UdpSendDelay::send. start: %lld, last: %lld, clock: %lld, pkt count: %d\n", m_startClock, m_lastClock, clock, m_pktCount);
    }

    return ms;
}

void UdpSendDelay::reset()
{
    m_startClock = 0;
    m_lastClock = 0;
    m_pktCount = 0;
}

void UdpSendDelay::delay(int fd, int ms)
{
    if (ms <= 0)
    {
        return;
    }

    if (fd <= 0)
    {
        delay(ms);
        return;
    }

    delay(ms);
}

void UdpSendDelay::delay(int ms)
{
    if (ms <= 0)
    {
        return;
    }

#ifdef WIN32
    Sleep(ms);
#else
    struct timespec req, rem;

    req.tv_sec = (time_t)(ms / 1000);
    req.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&req, &rem);
#endif //
}


#ifdef WIN32
inline unsigned __int64 CalculateMicroseconds(unsigned __int64 performancecount, unsigned __int64 performancefrequency)
{
    unsigned __int64 f = performancefrequency;
    unsigned __int64 a = performancecount;
    unsigned __int64 b = a / f;
    unsigned __int64 c = a%f; // a = b*f+c => (a*1000000)/f = b*1000000+(c*1000000)/f

    return b * 1000000ui64 + (c * 1000000ui64) / f;
}

static int64_t getCurrentTime()
{
    static int inited = 0;
    static unsigned __int64 microseconds, initmicroseconds;
    static LARGE_INTEGER performancefrequency;

    unsigned __int64 emulate_microseconds, microdiff;
    SYSTEMTIME systemtime;
    FILETIME filetime;

    LARGE_INTEGER performancecount;

    QueryPerformanceCounter(&performancecount);

    if (!inited) {
        inited = 1;
        QueryPerformanceFrequency(&performancefrequency);
        GetSystemTime(&systemtime);
        SystemTimeToFileTime(&systemtime, &filetime);
        microseconds = (((unsigned __int64)(filetime.dwHighDateTime) << 32) + (unsigned __int64)(filetime.dwLowDateTime)) / 10ui64;
        microseconds -= 11644473600000000ui64; // EPOCH
        initmicroseconds = CalculateMicroseconds(performancecount.QuadPart, performancefrequency.QuadPart);
    }

    emulate_microseconds = CalculateMicroseconds(performancecount.QuadPart, performancefrequency.QuadPart);

    microdiff = emulate_microseconds - initmicroseconds;

    return microseconds + microdiff;
}

#endif //


int64_t UdpSendDelay::getClock()
{
#ifdef WIN32
    return getCurrentTime();
#else
    struct timeval tv;
    gettimeofday(&tv, 0);
    return tv.tv_sec * 1000000 + tv.tv_usec;
#endif //
}


} /* namespace av */
